package com.cskaoyan.mall.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.mall.cart.api.dto.CartInfoDTO;
import com.cskaoyan.mall.common.constant.CodeEnum;
import com.cskaoyan.mall.common.constant.ResultCodeEnum;
import com.cskaoyan.mall.common.execption.BusinessException;
import com.cskaoyan.mall.common.result.Result;
import com.cskaoyan.mall.common.util.DateUtil;
import com.cskaoyan.mall.mq.constant.MqTopicConst;
import com.cskaoyan.mall.mq.producer.BaseProducer;
import com.cskaoyan.mall.order.client.*;
import com.cskaoyan.mall.order.constant.OrderStatus;
import com.cskaoyan.mall.order.constant.OrderType;
import com.cskaoyan.mall.order.converter.CartInfoConverter;
import com.cskaoyan.mall.order.converter.OrderDetailConverter;
import com.cskaoyan.mall.order.converter.OrderInfoConverter;
import com.cskaoyan.mall.order.dto.OrderDetailDTO;
import com.cskaoyan.mall.order.dto.OrderInfoDTO;
import com.cskaoyan.mall.order.dto.OrderTradeDTO;
import com.cskaoyan.mall.order.mapper.OrderDetailMapper;
import com.cskaoyan.mall.order.mapper.OrderInfoMapper;
import com.cskaoyan.mall.order.model.OrderDetail;
import com.cskaoyan.mall.order.model.OrderInfo;
import com.cskaoyan.mall.order.query.OrderDetailParam;
import com.cskaoyan.mall.order.query.OrderInfoParam;
import com.cskaoyan.mall.order.service.OrderService;
import com.cskaoyan.mall.pay.api.dto.PaymentInfoDTO;
import com.cskaoyan.mall.user.dto.UserAddressDTO;
import com.cskaoyan.mall.ware.api.dto.WareOrderTaskDTO;
import com.cskaoyan.mall.ware.api.dto.WareSkuDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Autowired
    UserApiClient userApiClient;
    @Autowired
    CartApiClient cartApiClient;
    @Autowired
    CartInfoConverter cartInfoConverter;
    @Autowired
    WareApiClient wareApiClient;
    @Autowired
    ProductApiClient productApiClient;
    @Autowired
    OrderInfoConverter orderInfoConverter;
    @Autowired
    OrderDetailConverter orderDetailConverter;
    @Autowired
    OrderDetailMapper orderDetailMapper;
    @Autowired
    OrderInfoMapper orderInfoMapper;
    @Autowired
    BaseProducer baseProducer;


    @Autowired
    PayApiClient payApiClient;


    @Override
    public Boolean checkPrice(Long skuId, BigDecimal skuPrice) {
        return null;
    }

    @Override
    public void refreshPrice(Long skuId, String userId) {
        cartApiClient.refreshCartPrice(userId,skuId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)//无论是否是runTimeException都会启动事务
    public Long saveOrderInfo(OrderInfo orderInfo) {
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        orderInfo.sumTotalAmount();
        orderInfo.setOutTradeNo("NO"+IdWorker.getId());
        orderInfo.setTradeBody(orderInfo.getOrderDetailList().get(0).getSkuName());
        //设置超时时间
        //TODO 在这个地方设置超时时间
        Date exTime= DateUtil.datePlusSeconds(new Date(),60);
        orderInfo.setExpireTime(exTime);

        int updateRows = orderInfoMapper.insert(orderInfo);
        if (updateRows<1){
            throw new BusinessException("添加订单信息失败", ResultCodeEnum.FAIL.getCode());
        }

        Long orderId=orderInfo.getId();
        //保存商品明细的数据
        for (OrderDetail orderDetail : orderInfo.getOrderDetailList()) {
            orderDetail.setOrderId(orderId);
            int updateDetailRows = orderDetailMapper.insert(orderDetail);
            if (updateDetailRows<1){
                throw new BusinessException("添加订单详细信息失败", ResultCodeEnum.FAIL.getCode());
            }
        }
        return orderId;
    }

    @Override
    public OrderInfoDTO getOrderInfo(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        OrderInfoDTO orderInfoDTO = orderInfoConverter.convertOrderInfoToOrderInfoDTO(orderInfo);
        Map<String,Object> searchMap=new HashMap<>();
        searchMap.put("order_id",orderId);
        List<OrderDetail> orderDetailList = orderDetailMapper.selectByMap(searchMap);
        List<OrderDetailDTO> orderDetailDTOS = orderDetailConverter.convertOrderDetailToDTOs(orderDetailList);
        orderInfoDTO.setOrderDetailList(orderDetailDTOS);
        return orderInfoDTO;
    }

    @Override
    public IPage<OrderInfoDTO> getPage(Page<OrderInfo> pageParam, String userId) {
        //查询信息，过滤
        LambdaQueryWrapper<OrderInfo> orderInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        orderInfoLambdaQueryWrapper.isNull(OrderInfo::getParentOrderId);
        orderInfoLambdaQueryWrapper.eq(OrderInfo::getUserId,userId);
        orderInfoMapper.selectPage(pageParam,orderInfoLambdaQueryWrapper);
        List<OrderInfo> records = pageParam.getRecords();
        List<OrderInfoDTO> orderInfoDTOS = orderInfoConverter.convertOrderInfoToOrderInfoDTOs(records);
        //然后查询它的子类
        for (OrderInfoDTO orderInfoDTO : orderInfoDTOS) {
            Map<String, Object> searchMap = new HashMap<>();
            searchMap.put("order_id",orderInfoDTO.getId());
            List<OrderDetail> orderDetailList = orderDetailMapper.selectByMap(searchMap);
            List<OrderDetailDTO> orderDetailDTOS = orderDetailConverter.convertOrderDetailToDTOs(orderDetailList);
            orderInfoDTO.setOrderDetailList(orderDetailDTOS);
            orderInfoDTO.setOrderStatusName(OrderStatus.getStatusDescByStatus(orderInfoDTO.getOrderStatus()));
        }
        Page<OrderInfoDTO> orderInfoDTOPage = cartInfoConverter.covertOrderInfoPageToDTO(pageParam,orderInfoDTOS);
        return orderInfoDTOPage;
    }

    @Override
    public void successPay(Long orderId) {
        log.info("order,运行支付成功的回调");
        //订单支付成功后需要订单操作的内容。
        //要先调用库存表删减库存
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        //修改订单状态
        orderInfo.setOrderStatus(OrderStatus.PAID.name());
        int updateRows = orderInfoMapper.updateById(orderInfo);
        if (updateRows<1){
            throw new BusinessException("更新订单状态失败",ResultCodeEnum.FAIL.getCode());
        }

        Result result = wareApiClient.decreaseStock(orderId);
        if (!ResultCodeEnum.SUCCESS.getCode().equals(result.getCode())){
            throw new BusinessException("删减库存呢失败",ResultCodeEnum.FAIL.getCode());
        }
    }

    //这个地方没有运行
    @Override
    public void successLockStock(String orderId, String taskStatus) {
        log.info("运行了库存的锁定");
        //这个地方是删减库存成功后的回调
        //这个taskStatus要判断是否是deducted
        String orderStatus="DEDUCTED".equals(taskStatus) ? OrderStatus.WAIT_DELEVER.name() : OrderStatus.STOCK_EXCEPTION.name();

        //然后更新
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        orderInfo.setOrderStatus(orderStatus);
        int updateRows = orderInfoMapper.updateById(orderInfo);
        if (updateRows<1){
            throw new BusinessException("修改订单状态异常",ResultCodeEnum.FAIL.getCode());
        }
    }

    @Override
    @Transactional
    public List<WareOrderTaskDTO> orderSplit(String orderId, List<WareSkuDTO> wareSkuDTOList) {
        //这里可以先把order的订单拆了
        //这里的逻辑是它子订单会复制父订单一份detail
        //获取对应的orderInfo和对应的detail

        OrderInfoDTO orderInfoDTO = getOrderInfo(Long.valueOf(orderId));
        if (orderInfoDTO==null){
            return new ArrayList<>();
        }

        //对应父节点的detail列表
        List<OrderDetailDTO> orderDetailList = orderInfoDTO.getOrderDetailList();

        List<WareOrderTaskDTO> wareOrderTaskDTOS = wareSkuDTOList.stream().map(wareSkuDTO -> {
            String wareId = wareSkuDTO.getWareId();
            List<String> skuIds = wareSkuDTO.getSkuIds();

            List<OrderDetail> subOrderDetailList = new ArrayList<>();
            for (OrderDetailDTO orderDetail : orderDetailList) {
                if (skuIds.contains(String.valueOf(orderDetail.getSkuId()))) {
                    //这个地方要复制一下
                    OrderDetail subOrderDetail = orderDetailConverter.convertOrderDetailToDTO(orderDetail);
                    subOrderDetail.setId(null);
                    subOrderDetailList.add(subOrderDetail);
                }
            }

            //此时如果这个数组内的值不为空，创建子列表和子详细信息
            if (!subOrderDetailList.isEmpty()) {
                OrderInfo subOrderInfo = orderInfoConverter.copyOrderInfo(orderInfoDTO);
                //然后改变子列表的相关属性值，改parentOrderId和修改时间
                subOrderInfo.setParentOrderId(Long.valueOf(orderId));
                subOrderInfo.setUpdateTime(new Date());
                subOrderInfo.setOrderDetailList(subOrderDetailList);
                subOrderInfo.setOutTradeNo("NO" + IdWorker.getId());
                subOrderInfo.setId(null);
                subOrderInfo.sumTotalAmount();
                subOrderInfo.setTradeBody(subOrderDetailList.get(0).getSkuName());

                //然后运行保存订单的接口
                 saveOrderInfo(subOrderInfo);
                subOrderInfo.setWareId(wareId);
                //这个地方要转换为需要的类型，然后返回
                WareOrderTaskDTO wareOrderTaskDTO = orderInfoConverter.convertOrderInfoToWareOrderTaskDTO(subOrderInfo);
                return wareOrderTaskDTO;
            }
            return null;

        }).filter(Objects::nonNull).collect(Collectors.toList());

        //然后要修改原订单的状态
        OrderInfo orderInfo=new OrderInfo();
        orderInfo.setOrderStatus(OrderStatus.SPLIT.name());
        orderInfo.setId(orderInfoDTO.getId());
        int updateRows = orderInfoMapper.updateById(orderInfo);
        if (updateRows<1){
            throw new RuntimeException("修改原订单内容失败");
        }

        return wareOrderTaskDTOS;
    }

    @Override
    @Transactional
    public void execExpiredOrder(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        //这个地方有个，，判断，要判断是否，在用户已经下单后，但没有传入回调的时候，取消了订单
        //先判断是否有对应的payInfo
        String outTradeNo = orderInfo.getOutTradeNo();
        PaymentInfoDTO paymentInfoDTOByOutTradeNo = payApiClient.getPaymentInfoDTOByOutTradeNo(outTradeNo);
        if (paymentInfoDTOByOutTradeNo!=null){
            //这个地方说明已经有支付记录了，需要做相关操作了
            //然后要远程调用支付宝的接口判断是否为已支付
            Result<String> alipayInfoResult = payApiClient.getAlipayInfo(outTradeNo);
            String status = alipayInfoResult.getData();
            //然后判断status的状态
            if (status==null||"TRADE_CLOSED".equals(status)){
                //用户没输入用户名密码或交易超时关闭了
                //关闭订单，关闭支付
                closeOrder(orderInfo);
                payApiClient.closePaymentInfo(outTradeNo);

                log.info("关闭订单/支付信息");
            }
            else if ("WAIT_BUYER_PAY".equals(status)){
                //用户没输入登录密码
                //关闭订单，关闭支付，关闭支付宝支付
                closeOrder(orderInfo);
                payApiClient.closePaymentInfo(outTradeNo);
                payApiClient.closeAlipay(outTradeNo);
                log.info("关闭订单/支付信息/关闭交易");
            }
            else if ("TRADE_SUCCESS".equals(status)){
                //付款成功,什么也不做
                log.info("付款成功");
            }
        }else {
            //如果不存在支付记录
            closeOrder(orderInfo);
        }



    }

    public void closeOrder(OrderInfo orderInfo){
        if (Objects.equals(orderInfo.getOrderStatus(), OrderStatus.UNPAID.name())){
            orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
            orderInfoMapper.updateById(orderInfo);
        }
    }

    @Override
    public Long saveSeckillOrder(OrderInfoParam orderInfoParam) {
        //这个地方要创建一个秒杀订单，并保存到数据库
        OrderInfo orderInfo = orderInfoConverter.convertOrderInfoParam(orderInfoParam);
        orderInfo.setOrderType(OrderType.PROMO_ORDER.name());
        orderInfo.getOrderDetailList().get(0).setId(null);
        Long orderId = this.saveOrderInfo(orderInfo);
        return orderId;
    }

    @Override
    public OrderTradeDTO getTradeInfo(String userId) {
        //服务间调用相关数据然后返回
        //这里需要在对应的服务加一下对应方法
        List<UserAddressDTO> userAddressListByUserId = userApiClient.findUserAddressListByUserId(userId);
        List<CartInfoDTO> cartCheckedList = cartApiClient.getCartCheckedList(userId);

        //然后计算总数和总价格即可
        int totalNum=0;
        BigDecimal totalAmount = new BigDecimal(0);

        for (CartInfoDTO cartInfoDTO : cartCheckedList) {
            totalNum+=cartInfoDTO.getSkuNum();
            BigDecimal curPrice=new BigDecimal(cartInfoDTO.getSkuNum()).multiply(cartInfoDTO.getSkuPrice());
            totalAmount=totalAmount.add(curPrice);
        }
        OrderTradeDTO orderTradeDTO=new OrderTradeDTO();
        orderTradeDTO.setTotalAmount(totalAmount);
        orderTradeDTO.setTotalNum(totalNum);
        List<OrderDetailDTO> orderDetailDTOList = cartInfoConverter.convertCartInfoDTOToOrderDetailDTOList(cartCheckedList);
        orderTradeDTO.setDetailArrayList(orderDetailDTOList);
        orderTradeDTO.setUserAddressList(userAddressListByUserId);


        return orderTradeDTO;
    }

    @Override
    public Long submitOrder(OrderInfoParam orderInfoParam, String userId) {
        //调用库存服务判断库存
        //先获取订单列表
        for (OrderDetailParam orderDetailParam : orderInfoParam.getOrderDetailList()) {
            //如果没库存就报错，要调用库存服务
            Result result = wareApiClient.hasStock(orderDetailParam.getSkuId(), orderDetailParam.getSkuNum());
            //失败
            if (result.getCode().equals(ResultCodeEnum.FAIL.getCode())){
                throw new BusinessException("库存不足"+orderDetailParam.getSkuName(),ResultCodeEnum.FAIL.getCode());
            }

            //然后判断价格是否变化了，如果变化了要修改购物车中的数据，然后报错
            BigDecimal skuPrice = productApiClient.getSkuPrice(orderDetailParam.getSkuId());
            if (!skuPrice.equals(orderDetailParam.getOrderPrice())){
                //如果不相等
                //修改相关价格，并报错
                cartApiClient.refreshCartPrice(userId,orderDetailParam.getSkuId());
                throw new BusinessException(orderDetailParam.getSkuNum()+"价格发生变化",ResultCodeEnum.FAIL.getCode());
            }
        }
        //然后要保存订单及订单详情
        //先存订单再存详情
        OrderInfo orderInfo = orderInfoConverter.convertOrderInfoParam(orderInfoParam);
        orderInfo.setUserId(Long.valueOf(userId));
        Long orderId = this.saveOrderInfo(orderInfo);


        //获取所有的skuId列表
        List<Long> skuIdList = orderInfo.getOrderDetailList().stream().map(OrderDetail::getSkuId).collect(Collectors.toList());
        //删除对应数据，要跨服务调用cart服务
        cartApiClient.removeCartProductsInOrder(userId,skuIdList);

        //然后下面要设置超时的回调
        //16 是30分钟  6是2分钟 5是一分钟
        System.out.println("创建了延时消息");
        //TODO 在这个地方设置超时时间
        baseProducer.sendDelayMessage(MqTopicConst.DELAY_ORDER_TOPIC,orderId,5);
        return orderId;
    }
}
